/**
 * 
 */
package edu.gtcfla.km.repositories.hibernate4;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import org.vsg.biz.BusinessEntityFactory;
import org.vsg.biz.account.User;
import org.vsg.biz.account.model.UserD;
import org.vsg.biz.utils.RepoInjectManager;
import org.vsg.common.orm.hibernate4.HibernateSessionManager;
import org.vsg.common.page.PaginationManager;
import org.vsg.search.api.page.PaginationException;

import edu.gtcfla.km.UserGroup;
import edu.gtcfla.km.model.CategoryD;
import edu.gtcfla.km.model.UserGroupD;
import edu.gtcfla.km.repositories.page.AvialableUserGroupProc;

/**
 * @author Bill Vison
 *
 */
@Repository(value="edu.gtcfla.km.repositories.userGroupRepo")
public class UserGroupRepoImpl<T extends UserGroup, ID extends Serializable>  implements edu.gtcfla.km.repositories.UserGroupRepo<T, ID> {

	
	@Autowired
	private ApplicationContext ctx;
	
	@Autowired	
	private BusinessEntityFactory entityFactory;
	
	@Autowired(required=false)
	private PaginationManager paginationManager;

	private HibernateSessionManager sessionManager;
	

	private static final Logger logger = LoggerFactory.getLogger(UserGroupRepoImpl.class);

	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		sessionManager = new HibernateSessionManager(sessionFactory);
	}		

	@Override
	public void save(T entity) {

		Session session = sessionManager.getSession();
	
		if (entity instanceof UserGroupD) {
			UserGroupD domainD = (UserGroupD)entity;

			ID id = (ID)session.save(domainD);
	
 			RepoInjectManager injectManager = new RepoInjectManager(ctx);		
			injectManager.injectSingle(entity);		
		}
		
	}


	@Override
	public void update(T entity) {
		Session session = sessionManager.getSession();
		
		if (entity instanceof UserGroupD) {
			UserGroupD domainD = (UserGroupD)entity;

			session.update(session.merge(domainD));
	
			RepoInjectManager injectManager = new RepoInjectManager(ctx);		
			injectManager.injectSingle(entity);		
		}		
	}


	@Override
	public void updateAll(Collection<T> entities) {
		// TODO Auto-generated method stub
		Session sess = sessionManager.getSession();

		for (T item : entities) {
			if ( ! ( item instanceof UserGroupD )) {
				sess.update( sess.merge( item ) );
			} else {
				sess.update (sess.merge( item ));
			}
		}
		
	}


	@Override
	public T findOne(ID primaryKey) {
		Session session = sessionManager.getSession();

		T entity = (T)session.get( UserGroupD.class, primaryKey);
		RepoInjectManager injectManager = new RepoInjectManager(ctx);		
		injectManager.injectSingle(entity);	
		
		return entity;
	}


	
	
	@Override
	public T loadOne(ID primaryKey) {
		Session session = sessionManager.getSession();

		T entity = (T)session.load( UserGroupD.class, primaryKey);
		RepoInjectManager injectManager = new RepoInjectManager(ctx);		
		injectManager.injectSingle(entity);	
		
		return entity;
	}

	@Override
	public T findParent(ID primaryKey) {
		StringBuilder query = new StringBuilder("select ");
		query.append("parent.id , parent.name , parent.createdDate , parent.createdUserId , ");
		query.append("from ").append(CategoryD.class.getName()).append(" as entity ");
		query.append("left join entity.parentD as parent ");

		query.append("where entity.id = :id"); 


		logger.debug("query : " + query.toString());
		
		Session sess = sessionManager.getSession();
		
		Query hql = sess.createQuery( query.toString() );
		hql.setParameter("id", primaryKey);

		Iterator<Object[]> iterRes = (Iterator<Object[]>) hql.iterate();
		
		T result = null;
		
		RepoInjectManager injectManager = new RepoInjectManager(ctx);		
	
		if (iterRes.hasNext()) {
			Object[] fields = (Object[])iterRes.next();
			// --- convert to dto ----
			CategoryD entity = new CategoryD();
			int i = 0;
			entity.setId( (String)fields[i++] );
			entity.setName( (String)fields[i++] );
			entity.setCreatedDate( (Date)fields[i++] );
			entity.setCreatedUserId( (String)fields[i++] );
			
			//entity.setParentD( (CategoryD)fields[i++] ); 
			
			// --- convert to dto ----
			injectManager.injectSingle( entity );
			
			result = (T)entity;

		}
		return result;
	}


	@Override
	public Map<String, T> findAllMap() {
		StringBuilder query = new StringBuilder("select ");
		query.append("entity.id , entity.groupName , entity.description ");
		query.append("from ").append(UserGroupD.class.getName()).append(" as entity ");

		logger.debug("query : " + query.toString());
		
		Session sess = sessionManager.getSession();
		
		Query hql = sess.createQuery( query.toString() );

		Iterator<Object[]> iterRes = (Iterator<Object[]>) hql.iterate();
		
		Map<String , T> resultMap = new LinkedHashMap<String , T>();
		RepoInjectManager injectManager = new RepoInjectManager(ctx);	
		while (iterRes.hasNext()) {
			Object[] fields = iterRes.next();
			
			UserGroupD entity = new UserGroupD();
			int i = 0;
			entity.setId( (String)fields[i++] );
			entity.setGroupName( (String)fields[i++] );
			entity.setDescription( (String)fields[i++] );
			
			// --- convert to dto ----
			injectManager.injectSingle( entity );
			resultMap.put( entity.getId() ,(T) entity);

		}
				
		return resultMap;
	}

	@Override
	public Map<String, T> findAllMap(String queryParam) {
		StringBuilder query = new StringBuilder("select ");
		query.append("entity.id , entity.groupName , entity.description ");
		query.append("from ").append(UserGroupD.class.getName()).append(" as entity ");
		
		if (queryParam != null) {
			query.append("where entity.groupName like :query ");
		}

		logger.debug("query : " + query.toString());
		
		Session sess = sessionManager.getSession();
		
		Query hql = sess.createQuery( query.toString() );
		if ( queryParam != null ) {
			hql.setString( "query" , queryParam);
		}

		Iterator<Object[]> iterRes = (Iterator<Object[]>) hql.iterate();
		
		Map<String , T> resultMap = new LinkedHashMap<String , T>();
		RepoInjectManager injectManager = new RepoInjectManager(ctx);	
		while (iterRes.hasNext()) {
			Object[] fields = iterRes.next();
			
			UserGroupD entity = new UserGroupD();
			int i = 0;
			entity.setId( (String)fields[i++] );
			entity.setGroupName( (String)fields[i++] );
			entity.setDescription( (String)fields[i++] );
			
			// --- convert to dto ----
			injectManager.injectSingle( entity );
			resultMap.put( entity.getId() ,(T) entity);

		}
				
		return resultMap;
	}
	
	
	@Override
	public Map<String, T> findAllByIds(Set<String> ids) {
		// TODO Auto-generated method stub
		StringBuilder query = new StringBuilder("select ");
		query.append("entity.id , entity.groupName , entity.description ");
		query.append("from ").append(UserGroupD.class.getName()).append(" as entity ");
		query.append("where entity.id in (:ids) ");


		logger.debug("query : " + query.toString());
		
		Session sess = sessionManager.getSession();
		
		Query hql = sess.createQuery( query.toString() );
		if (ids.isEmpty()) {
			ids.add("");
		} 
		hql.setParameterList("ids", ids);

		Iterator<Object[]> iterRes = (Iterator<Object[]>) hql.iterate();
		
		Map<String , T> resultMap = new LinkedHashMap<String , T>();
		RepoInjectManager injectManager = new RepoInjectManager(ctx);	
		while (iterRes.hasNext()) {
			Object[] fields = iterRes.next();
			
			UserGroupD entity = new UserGroupD();
			int i = 0;
			
			entity.setId( (String)fields[i++] );
			entity.setGroupName((String)fields[i++] );
			entity.setDescription( (String)fields[i++] );

			
			// --- convert to dto ----
			injectManager.injectSingle( entity );
			resultMap.put( entity.getId() ,(T) entity);

		}
				
		return resultMap;
	}

	@Override
	public void deleteFully(T entity) {
		// TODO Auto-generated method stub
		Session sess = sessionManager.getSession();
		sess.delete( entity );
	}

	@Override
	public Page<T> pageAviableUserGroup(Pageable pageable) {
		// TODO Auto-generated method stub
		AvialableUserGroupProc proc = new AvialableUserGroupProc();
		
		Page<T> page= null;
		try {
			page = paginationManager.createPage(pageable, proc);
		} catch (PaginationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return page;
	}

	@Override
	public T findFirstOneByUserId(String userId) {
		
		StringBuilder query = new StringBuilder("select ");
		query.append("entity.id , entity.groupName , entity.description ");
		query.append("from ").append(UserGroupD.class.getName()).append(" as entity ");
		query.append("inner join entity.memberD as mber ");		
		query.append("where mber.id = :userId ");


		logger.debug("query : " + query.toString());
		
		Session sess = sessionManager.getSession();
		
		Query hql = sess.createQuery( query.toString() );
		hql.setString("userId", userId);

		Iterator<Object[]> iterRes = (Iterator<Object[]>) hql.iterate();
		
		T result = null;
		
		RepoInjectManager injectManager = new RepoInjectManager(ctx);	
		if (iterRes.hasNext()) {
			Object[] fields = iterRes.next();
			
			UserGroupD entity = new UserGroupD();
			int i = 0;
			
			entity.setId( (String)fields[i++] );
			entity.setGroupName((String)fields[i++] );
			entity.setDescription( (String)fields[i++] );

			
			// --- convert to dto ----
			injectManager.injectSingle( entity );
			
			result = (T)entity;

		}
		return result;
	}

	@Override
	public Set<User> findMembersByGroupId(String groupId) {
		// TODO Auto-generated method stub
		StringBuilder query = new StringBuilder("select ");
		// --- set fields --- 
		query.append("entity.id , entity.expiredDate ,  entity.createdDate , entity.createdUserId , entity.email , ");
		query.append("entity.mobilephone , entity.name , entity.officephone , entity.ownedUserId , ");
		query.append("entity.qq , entity.updatedDate , entity.updatedUserId ");

		query.append("from ").append(UserGroupD.class.getName()).append(" as grp ");
		query.append("inner join grp.memberD as entity ");
		
		query.append("where grp.id = :groupId ");


		logger.info("query : " + query.toString());
		
		Session sess = sessionManager.getSession();
		
		Query hql = sess.createQuery( query.toString() );
		hql.setString("groupId", groupId);

		Iterator<Object[]> iterRes = (Iterator<Object[]>) hql.iterate();

		Set<User> result = new LinkedHashSet<User>();
		
		
		UserD entity = null;
		while (iterRes.hasNext()) {
			Object[] fields = (Object[]) iterRes.next();
			// --- convert to dto ----
			entity = new UserD();
			
			int i = 0;
			entity.setId( fields[i++].toString() );
			entity.setExpiredDate((Date)fields[i++]  );
			entity.setCreatedDate( (Date)fields[i++]  );
			entity.setCreatedUserId( (String) fields[i++] );
			entity.setEmail( (String) fields[i++]  );

			entity.setMobilephone((String)fields[i++] );
			entity.setName( ( String)fields[i++] );
			entity.setOfficephone( ( String)fields[i++] );
			entity.setOwnedUserId( ( String)fields[i++] );

			entity.setQq( (String)fields[i++] );
			entity.setUpdatedDate((Date)fields[i++] );
			entity.setUpdatedUserId( (String)fields[i++]  );
			
			result.add((User)entity);
		}
		
		return result;
	}

	@Override
	public Set<T> findAllByUserId(String userId) {
		
		StringBuilder query = new StringBuilder("select ");
		query.append("entity.id , entity.groupName , entity.description ");
		query.append("from ").append(UserGroupD.class.getName()).append(" as entity ");
		query.append("inner join entity.memberD as member ");		
		query.append("where member.id = :userId ");


		logger.debug("query : " + query.toString());
		
		Session sess = sessionManager.getSession();
		
		Query hql = sess.createQuery( query.toString() );
		hql.setString("userId", userId);

		Iterator<Object[]> iterRes = (Iterator<Object[]>) hql.iterate();
		
		Set<T> resultSet = new LinkedHashSet<T>();
		RepoInjectManager injectManager = new RepoInjectManager(ctx);	
		while (iterRes.hasNext()) {
			Object[] fields = iterRes.next();
			
			UserGroupD entity = new UserGroupD();
			int i = 0;
			
			entity.setId( (String)fields[i++] );
			entity.setGroupName((String)fields[i++] );
			entity.setDescription( (String)fields[i++] );

			
			// --- convert to dto ----
			injectManager.injectSingle( entity );
			resultSet.add( (T) entity );

		}
		return resultSet;
				
	}

	@Override
	public void assignMembersToUserGroup(Set<String> newMemberIds, String groupId) {
		// TODO Auto-generated method stub
		Session session = sessionManager.getSession();
		UserGroupD entity = (UserGroupD)session.get( UserGroupD.class, groupId);
		
	
		
		// --- get new user ---
		StringBuilder query = new StringBuilder("select ");
		// --- set fields --- 
		query.append("entity ");
		query.append("from ").append(UserD.class.getName()).append(" as entity ");
		query.append("where entity.id in (:ids) ");

		logger.debug("query : " + query.toString());
		Query hql = session.createQuery( query.toString() );
		if (newMemberIds.isEmpty()) {
			newMemberIds.add("");
		}
		hql.setParameterList("ids", newMemberIds);
		

		Iterator<UserD> iterRes = (Iterator<UserD>) hql.iterate();

		Set<UserD> newMembers = new LinkedHashSet<UserD>();
		
		UserD userModel = null;
		while (iterRes.hasNext()) {
			userModel = (UserD) iterRes.next();
			// --- convert to dto ----
			newMembers.add(userModel);
		}
		
		
		
		// --- find all member ---
		query = new StringBuilder("select ");
		// --- set fields --- 
		query.append("entity ");

		query.append("from ").append(UserGroupD.class.getName()).append(" as grp ");
		query.append("inner join grp.memberD as entity ");
		query.append("where grp.id = :groupId ");

		logger.info("query : " + query.toString());
		
		Session sess = sessionManager.getSession();
		
		hql = sess.createQuery( query.toString() );
		hql.setString("groupId", groupId);

		Iterator<UserD> iterResOut = (Iterator<UserD>) hql.iterate();
		//Set<User> result = new LinkedHashSet<User>();
		UserD user = null;
		while (iterResOut.hasNext()) {
			user = (UserD) iterResOut.next();
			
			if (!newMembers.contains( user )) {
				entity.getMemberD().remove( user );
			}

		}
		
		
		if (logger.isDebugEnabled()) {
			// --- trace role ---
			for (UserD userItem : newMembers) {
				logger.info( "trace : " + userItem.getId() + " , " + userItem.getAssignedRolesSet() );
			}
		}
		
		
		entity.setMemberD(newMembers);
		
		session.update( entity );



		
	}



	
	

	
}
